//============================================================================
/// \file   DockFocusController.cpp
/// \author Uwe Kindler
/// \date   05.06.2020
/// \brief  Implementation of CDockFocusController class
//============================================================================

//============================================================================
//                                   INCLUDES
//============================================================================
#include <AutoHideTab.h>
#include "DockFocusController.h"

#include <algorithm>
#include <iostream>

#include <QPointer>
#include <QApplication>
#include <QAbstractButton>
#include <QWindow>

#include "DockWidget.h"
#include "DockAreaWidget.h"
#include "DockWidgetTab.h"
#include "DockContainerWidget.h"
#include "FloatingDockContainer.h"
#include "DockManager.h"
#include "DockAreaTitleBar.h"

#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
#include "linux/FloatingWidgetTitleBar.h"
#endif

namespace ads
{
static const char* const FocusedDockWidgetProperty = "FocusedDockWidget";

下面是更易读的代码，其中添加了必要的注释：

/**
 * CDockFocusController类的私有数据类（pimpl）
 */
struct DockFocusControllerPrivate
{
    CDockFocusController *_this;
    QPointer<CDockWidget> FocusedDockWidget = nullptr; // 当前聚焦的CDockWidget
    QPointer<CDockAreaWidget> FocusedArea = nullptr; // 当前聚焦的CDockAreaWidget
    QPointer<CDockWidget> OldFocusedDockWidget = nullptr; // 之前聚焦的CDockWidget
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
    QPointer<CFloatingDockContainer> FloatingWidget = nullptr; // 浮动窗口
#endif
    CDockManager* DockManager; // CDockManager
    bool ForceFocusChangedSignal = false; // 是否强制发出信号
    bool TabPressed = false; // 是否按下Tab键

    /**
     * 私有数据类的构造函数
     */
    DockFocusControllerPrivate(CDockFocusController *_public);

    /**
     * 更新给定dock widget和它所属的dock area的焦点样式
     */
    void updateDockWidgetFocus(CDockWidget* DockWidget);
};

// 以下是辅助函数，用于更新聚焦样式

/**
 * 更新给定dock widget的焦点样式
 */
static void updateDockWidgetFocusStyle(CDockWidget* DockWidget, bool Focused)
{
    DockWidget->setProperty("focused", Focused);
    DockWidget->tabWidget()->setProperty("focused", Focused);
    DockWidget->tabWidget()->updateStyle();
    internal::repolishStyle(DockWidget);
}

/**
 * 更新给定dock area的焦点样式
 */
static void updateDockAreaFocusStyle(CDockAreaWidget* DockArea, bool Focused)
{
    DockArea->setProperty("focused", Focused);
    internal::repolishStyle(DockArea);
    internal::repolishStyle(DockArea->titleBar());
}

#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
/**
 * 更新浮动窗口的焦点样式
 */
static void updateFloatingWidgetFocusStyle(CFloatingDockContainer* FloatingWidget, bool Focused)
{
    if (FloatingWidget->hasNativeTitleBar())
    {
        return;
    }
    auto TitleBar = qobject_cast<CFloatingWidgetTitleBar*>(FloatingWidget->titleBarWidget());
    if (!TitleBar)
    {
        return;
    }
    TitleBar->setProperty("focused", Focused);
    TitleBar->updateStyle();
}
#endif

DockFocusControllerPrivate::DockFocusControllerPrivate(CDockFocusController *_public) :
    _this(_public)
{
}

// 更新DockWidget的焦点
void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget)
{
    // 如果DockWidget不可获得焦点，则返回
    if (!DockWidget->features().testFlag(CDockWidget::DockWidgetFocusable))
    {
        return;
    }
    
    QWindow* Window = nullptr;
    auto DockContainer = DockWidget->dockContainer();
    
    // 如果DockContainer存在，则获取其所属的窗口
    if (DockContainer)
    {
        Window = DockContainer->window()->windowHandle();
    }
    
    // 如果窗口存在，则将DockWidget设置为窗口的属性
    if (Window)
    {
        Window->setProperty(FocusedDockWidgetProperty, QVariant::fromValue(QPointer<CDockWidget>(DockWidget)));
    }
    
    CDockAreaWidget* NewFocusedDockArea = nullptr;
    
    // 如果当前有焦点的DockWidget存在，则取消其焦点样式
    if (FocusedDockWidget)
    {
        updateDockWidgetFocusStyle(FocusedDockWidget, false);
    }
    
    CDockWidget* old = FocusedDockWidget;
    
    // 更新当前焦点的DockWidget
    FocusedDockWidget = DockWidget;
    
    // 设置新的焦点DockWidget的焦点样式
    updateDockWidgetFocusStyle(FocusedDockWidget, true);
    
    // 获取新的焦点DockWidget所属的DockAreaWidget
    NewFocusedDockArea = FocusedDockWidget->dockAreaWidget();
    
    // 如果新的焦点DockWidget所属的DockAreaWidget存在且与当前焦点不同，则更新焦点样式
    if (NewFocusedDockArea && (FocusedArea != NewFocusedDockArea))
    {
        if (FocusedArea)
        {
            QObject::disconnect(FocusedArea, SIGNAL(viewToggled(bool)), _this, SLOT(onFocusedDockAreaViewToggled(bool)));
            updateDockAreaFocusStyle(FocusedArea, false);
        }
        
        FocusedArea = NewFocusedDockArea;
        updateDockAreaFocusStyle(FocusedArea, true);
        QObject::connect(FocusedArea, SIGNAL(viewToggled(bool)), _this, SLOT(onFocusedDockAreaViewToggled(bool)));
    }
    
    CFloatingDockContainer* NewFloatingWidget = nullptr;
    DockContainer = FocusedDockWidget->dockContainer();
    
    // 如果DockContainer存在，则获取其所属的FloatingWidget
    if (DockContainer)
    {
        NewFloatingWidget = DockContainer->floatingWidget();
    }
    
    // 如果新的FloatingWidget存在，则将DockWidget设置为其属性
    if (NewFloatingWidget)
    {
        NewFloatingWidget->setProperty(FocusedDockWidgetProperty, QVariant::fromValue(QPointer<CDockWidget>(DockWidget)));
    }
    
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
    // 这段代码用于根据当前的焦点状态为linux样式浮动窗口标题栏添加样式
    if (FloatingWidget != NewFloatingWidget)
    {
        if (FloatingWidget)
        {
            updateFloatingWidgetFocusStyle(FloatingWidget, false);
        }
        
        FloatingWidget = NewFloatingWidget;
        
        if (FloatingWidget)
        {
            updateFloatingWidgetFocusStyle(FloatingWidget, true);
        }
    }
#endif
    
    // 如果old与DockWidget相同且未强制更改焦点信号，则返回
    if (old == DockWidget && !ForceFocusChangedSignal)
    {
        return;
    }
    
    ForceFocusChangedSignal = false;
    
    // 如果DockWidget可见，则发出focusedDockWidgetChanged信号
    if (DockWidget->isVisible())
    {
        Q_EMIT DockManager->focusedDockWidgetChanged(old, DockWidget);
    }
    else
    {
        OldFocusedDockWidget = old;
        QObject::connect(DockWidget, SIGNAL(visibilityChanged(bool)), _this, SLOT(onDockWidgetVisibilityChanged(bool)));
    }
}



//============================================================================
// 当DockWidget的可见性发生变化时调用的槽函数
void CDockFocusController::onDockWidgetVisibilityChanged(bool Visible)
{
    auto Sender = sender();
    auto DockWidget = qobject_cast<ads::CDockWidget*>(Sender);
    disconnect(Sender, SIGNAL(visibilityChanged(bool)), this, SLOT(onDockWidgetVisibilityChanged(bool)));
    if (DockWidget && Visible)
    {
        Q_EMIT d->DockManager->focusedDockWidgetChanged(d->OldFocusedDockWidget, DockWidget);
    }
}

//============================================================================
// CDockFocusController的构造函数
CDockFocusController::CDockFocusController(CDockManager* DockManager) :
    Super(DockManager),
    d(new DockFocusControllerPrivate(this))
{
    d->DockManager = DockManager;
    connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)),
            this, SLOT(onApplicationFocusChanged(QWidget*, QWidget*)));
    connect(QApplication::instance(), SIGNAL(focusWindowChanged(QWindow*)),
            this, SLOT(onFocusWindowChanged(QWindow*)));
    connect(d->DockManager, SIGNAL(stateRestored()), SLOT(onStateRestored()));
}

//============================================================================
// CDockFocusController的析构函数
CDockFocusController::~CDockFocusController()
{
    delete d;
}

//============================================================================
// 当焦点窗口发生变化时调用的槽函数
void CDockFocusController::onFocusWindowChanged(QWindow *focusWindow)
{
    if (!focusWindow)
    {
        return;
    }

    auto vDockWidget = focusWindow->property(FocusedDockWidgetProperty);
    if (!vDockWidget.isValid())
    {
        return;
    }

    auto DockWidget = vDockWidget.value<QPointer<CDockWidget>>();
    if (!DockWidget)
    {
        return;
    }

    d->updateDockWidgetFocus(DockWidget);
}

//===========================================================================
// 当应用程序焦点发生变化时调用的槽函数
void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidget* focusedNow)
{
    Q_UNUSED(focusedOld);

    // 如果正在恢复状态或者用户点击了导致焦点变化的选项卡，则忽略焦点变化
    if (d->DockManager->isRestoringState() || d->TabPressed)
    {
        return;
    }

    ADS_PRINT("CDockFocusController::onApplicationFocusChanged "
        << " old: " << focusedOld << " new: " << focusedNow);
    if (!focusedNow)
    {
        return;
    }

    CDockWidget* DockWidget = qobject_cast<CDockWidget*>(focusedNow);
    if (!DockWidget)
    {
        DockWidget = internal::findParent<CDockWidget*>(focusedNow);
    }

#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
    if (!DockWidget)
    {
        return;
    }
#else
    if (!DockWidget || DockWidget->tabWidget()->isHidden())
    {
        return;
    }
#endif

    d->updateDockWidgetFocus(DockWidget);
}


//===========================================================================
// 设置当前选中的dock widget的tab
void CDockFocusController::setDockWidgetTabFocused(CDockWidgetTab* Tab)
{
    auto DockWidget = Tab->dockWidget();
    if (DockWidget)
    {
        d->updateDockWidgetFocus(DockWidget);
    }
}
//===========================================================================
// 清除dock widget的焦点
void CDockFocusController::clearDockWidgetFocus(CDockWidget* dockWidget)
{
    dockWidget->clearFocus();
    updateDockWidgetFocusStyle(dockWidget, false);
}
//===========================================================================
// 设置当前选中的dock widget
void CDockFocusController::setDockWidgetFocused(CDockWidget* focusedNow)
{
    d->updateDockWidgetFocus(focusedNow);
}
//===========================================================================
// 当焦点所在的dock area被切换时触发
void CDockFocusController::onFocusedDockAreaViewToggled(bool Open)
{
    if (d->DockManager->isRestoringState())
    {
        return;
    }
    CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(sender());
    if (!DockArea || Open)
    {
        return;
    }
    auto Container = DockArea->dockContainer();
    auto OpenedDockAreas = Container->openedDockAreas();
    if (OpenedDockAreas.isEmpty())
    {
        return;
    }
    d->updateDockWidgetFocus(OpenedDockAreas[0]->currentDockWidget());
}
//===========================================================================
// 通知widget或area发生位置改变
void CDockFocusController::notifyWidgetOrAreaRelocation(QWidget* DroppedWidget)
{
    if (d->DockManager->isRestoringState())
    {
        return;
    }
    CDockWidget* DockWidget = qobject_cast<CDockWidget*>(DroppedWidget);
    if (!DockWidget)
    {
        CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(DroppedWidget);
        if (DockArea)
        {
            DockWidget = DockArea->currentDockWidget();
        }
    }
    if (!DockWidget)
    {
        return;
    }
    d->ForceFocusChangedSignal = true;
    CDockManager::setWidgetFocus(DockWidget);
}
//===========================================================================
// 通知浮动窗口被放置
void CDockFocusController::notifyFloatingWidgetDrop(CFloatingDockContainer* FloatingWidget)
{
    if (!FloatingWidget || d->DockManager->isRestoringState())
    {
        return;
    }
    auto vDockWidget = FloatingWidget->property(FocusedDockWidgetProperty);
    if (!vDockWidget.isValid())
    {
        return;
    }
    auto DockWidget = vDockWidget.value<QPointer<CDockWidget>>();
    if (DockWidget)
    {
        DockWidget->dockAreaWidget()->setCurrentDockWidget(DockWidget);
        CDockManager::setWidgetFocus(DockWidget);
    }
}

//==========================================================================
// 当状态恢复时调用
void CDockFocusController::onStateRestored()
{
    // 如果有焦点的dock窗口
    if (d->FocusedDockWidget)
    {
        // 更新dock窗口的焦点样式
        updateDockWidgetFocusStyle(d->FocusedDockWidget, false);
    }
}
//==========================================================================
// 返回焦点的dock窗口
CDockWidget* CDockFocusController::focusedDockWidget() const
{
    return d->FocusedDockWidget.data();
}
//==========================================================================
// 设置dock窗口的Tab键是否按下
void CDockFocusController::setDockWidgetTabPressed(bool Value)
{
    d->TabPressed = Value;
}
} // namespace ads

//---------------------------------------------------------------------------
// EOF DockFocusController.cpp
